package com.gemdale.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.gemdale.action.ApiActionModel;
import com.gemdale.api_object.ApiObjectModel;
import com.gemdale.step.AssertModel;
import com.gemdale.step.TestcaseStepModel;
import com.gemdale.testcase.TestcaseModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * @author: JJJJ
 * @date:2021/6/17 10:02
 * @Description:
 */
public class CreateTestcaseUtils {

    static Logger logger = LoggerFactory.getLogger(CreateTestcaseUtils.class);
    /**
     * 根据接口自动生成所有方法的测试用例模板
     * @param apiDirPath api文件路径
     * @param testcaseDirPath 存放生成的测试用例地址
     * @param apiActionName 指定单个接口方法名称  无则代表生成全部方法的用例
     */
    public static void AutoCreateTestCase(String apiDirPath,String testcaseDirPath,String apiActionName) throws Exception {
        // 1. 先对yaml数据进行反序列化获取到接口数据
        ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
        ApiObjectModel apiObjectModel = objectMapper.readValue(new File(apiDirPath), ApiObjectModel.class);

        // 获取接口及中的方法集合
        HashMap<String, ApiActionModel> actions = apiObjectModel.getActions();
        // 创建testcaseModel 后续要序列化成yaml文件
        TestcaseModel testcaseModel = new TestcaseModel();
        // 创建testcaseStepModel 用于testcaseModel封装
        TestcaseStepModel testcaseStepModel = new TestcaseStepModel();
        // 声明变量用于存放testcaseStepModel
        ArrayList<TestcaseStepModel> steps = new ArrayList<>();
        //遍历方法集  key:方法名称  value是一个ApiActionModel对象
        if (apiActionName != null){

            ApiActionModel apiActionModel = actions.get(apiActionName);
            logger.info("生成方法名为"+apiActionName+"的测试用例模板");
            createTestcase(testcaseDirPath, objectMapper, apiObjectModel, testcaseModel, testcaseStepModel, steps, apiActionName, apiActionModel);
        }
        if (apiActionName==null){
            actions.forEach((key,value) ->{
                logger.info("生成方法名为"+key+"的测试用例模板");
                createTestcase(testcaseDirPath, objectMapper, apiObjectModel, testcaseModel, testcaseStepModel, steps, key, value);
            });
        }
    }

    /**
     * 打包测试用例对象 进行反序列化
     * @param testcaseDirPath 测试用例存放路径
     * @param objectMapper objectMapper对象
     * @param apiObjectModel 接口模块对象
     * @param testcaseModel 测试用例模块对象
     * @param testcaseStepModel  测试用例步骤对象
     * @param steps 测试用例步骤集合
     * @param apiActionName  接口方法名
     * @param actionModel 借口模块对象
     */
    private static void createTestcase(String testcaseDirPath, ObjectMapper objectMapper, ApiObjectModel apiObjectModel, TestcaseModel testcaseModel, TestcaseStepModel testcaseStepModel, ArrayList<TestcaseStepModel> steps, String apiActionName, ApiActionModel actionModel) {
        // 封装testcaseStepModel
        packageTestStepModel(apiObjectModel, testcaseStepModel, apiActionName, actionModel);
        // 需要先进行数据清除避免影响下一个用例封装
        steps.clear();
        steps.add(testcaseStepModel);
        // 封装testcaseModel
        packageTestcaseModel(testcaseModel, steps, apiActionName);
        try {
            // 将测试用例进行序列化成yaml文件
            objectMapper.writeValue(new File(testcaseDirPath + "/" + apiActionName + ".yaml"), testcaseModel);
        } catch (IOException e) {
            logger.error("生成yaml文件失败");
            e.printStackTrace();
        }
    }

    /**
     * 打包封装testcaseStepModel
     * @param apiObjectModel  接口集合对象
     * @param testcaseStepModel 测试用例步骤对象
     * @param apiActionName 接口方法名
     * @param actionModel 接口方法对象
     */
    private static void packageTestStepModel(ApiObjectModel apiObjectModel, TestcaseStepModel testcaseStepModel, String apiActionName, ApiActionModel actionModel) {
        // 设置所调用的接口集
        testcaseStepModel.setApi(apiObjectModel.getName());
        // 设置用例调用的具体方法
        testcaseStepModel.setAction(apiActionName);
        // 进行形参:实参封装
        testcaseStepModel.setActualParameterMap(getActualParameterMap(actionModel));
        testcaseStepModel.setId(0);
        testcaseStepModel.setAsserts(getAssertModels(apiActionName));
    }

    /**
     * 获取实参集合
     * @param apiActionModel 接口方法模块对象
     */
    private static ArrayList<HashMap<String, String>> getActualParameterMap(ApiActionModel apiActionModel){
        // 若query数据不为空直接进行返回
        ArrayList<HashMap<String, String>> query = apiActionModel.getQuery();
        if (query !=null){
            return query;
        }
        // 若body数据不为空 则需要先进行实参集合格式封装后再进行返回
        String body = apiActionModel.getBody();
        if (body !=null){
            ArrayList<String> formalParam = new ArrayList<>();
            CustomStrUtils.getFormalParam(body,formalParam);
            ArrayList<HashMap<String, String>> actualParameterMap = new ArrayList<>();
            for (String param : formalParam) {
                HashMap<String, String> paramMap = new HashMap<>();
                paramMap.put(param,"${"+param+"}");
                actualParameterMap.add(paramMap);
            }
            return actualParameterMap;
        }
        return null;
    }

    /**
     * 封装断言格式数据
     */
    private static ArrayList<AssertModel> getAssertModels(String apiActionName) {
        ArrayList<AssertModel> asserts = new ArrayList<>();
        AssertModel assertModel = new AssertModel();
        assertModel.setMatcher("equalTo");
        assertModel.setActual("code");
        assertModel.setExpect("0");
        assertModel.setReason(apiActionName +" 基础校验!");
        asserts.add(assertModel);
        return asserts;
    }

    /**
     * 封装testcaseModel
     * @param testcaseModel 测试用例模块
     * @param steps  测试用例步骤
     * @param apiActionName 测试用例所调用接口方法
     */
    private static void packageTestcaseModel(TestcaseModel testcaseModel, ArrayList<TestcaseStepModel> steps, String apiActionName) {
        // 设置用例名称
        testcaseModel.setName(apiActionName +"Case");
        // 设置用例description
        testcaseModel.setDescription(apiActionName +"测试用例");
        // 封装测试步骤
        testcaseModel.setSteps(steps);
    }
}
